001 /* 002 * Copyright (c) 2005 Stephen J. McConnell 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 013 * implied. 014 * 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019 package net.dpml.lang; 020 021 import java.io.IOException; 022 import java.util.ArrayList; 023 import java.net.URL; 024 import java.net.URI; 025 import java.net.URLClassLoader; 026 027 import net.dpml.transit.Artifact; 028 029 import net.dpml.util.Logger; 030 031 /** 032 * A named classloader. 033 * 034 * @author <a href="http://www.dpml.net">Digital Product Meta Library</a> 035 * @version 1.0.1 036 */ 037 public class StandardClassLoader extends URLClassLoader 038 { 039 //-------------------------------------------------------------------- 040 // static 041 //-------------------------------------------------------------------- 042 043 /** 044 * Internal utility class to build a classloader. If the supplied url 045 * sequence is zero length the parent classloader is returned directly. 046 * 047 * @param logger the logging channel 048 * @param name the name identifying the classloader 049 * @param category the category that this classloader is handling 050 * @param parent the parent classloader 051 * @param uris the uris to assign as classloader content 052 * @return the classloader 053 * @exception IOException if an I/O error occurs 054 */ 055 public static ClassLoader buildClassLoader( Logger logger, String name, Category category, ClassLoader parent, URI[] uris ) 056 throws IOException 057 { 058 URL[] urls = toURLs( uris ); 059 if( 0 == urls.length ) 060 { 061 return parent; 062 } 063 ArrayList list = new ArrayList(); 064 for( int i=0; i < urls.length; i++ ) 065 { 066 if( isaCandidate( parent, urls[i] ) ) 067 { 068 list.add( urls[i] ); 069 } 070 } 071 URL[] qualified = (URL[]) list.toArray( new URL[0] ); 072 if( qualified.length == 0 ) 073 { 074 return parent; 075 } 076 else 077 { 078 ClassLoader loader = 079 new StandardClassLoader( name, category, qualified, parent ); 080 classloaderConstructed( logger, name, category, loader ); 081 return loader; 082 } 083 } 084 085 /** 086 * Convert a sequence of URIs to URLs. 087 * @param uris the uris to convert 088 * @return the corresponding urls 089 * @exception IOException of a transformation error occurs 090 */ 091 public static URL[] toURLs( URI[] uris ) throws IOException 092 { 093 URL[] urls = new URL[ uris.length ]; 094 for( int i=0; i < urls.length; i++ ) 095 { 096 URI uri = uris[i]; 097 if( Artifact.isRecognized( uri ) ) 098 { 099 urls[i] = Artifact.toURL( uri ); 100 } 101 else 102 { 103 urls[i] = uri.toURL(); 104 } 105 } 106 return urls; 107 } 108 109 /** 110 * Test if the supplied url is already present within the supplied classloader. 111 * @param classloader the classloader to validate against 112 * @param url to url to check for 113 * @return true if the url is not included in the classloader 114 */ 115 private static boolean isaCandidate( ClassLoader classloader, URL url ) 116 { 117 if( classloader instanceof URLClassLoader ) 118 { 119 URL[] urls = ( (URLClassLoader) classloader ).getURLs(); 120 for( int i=0; i < urls.length; i++ ) 121 { 122 if( urls[i].equals( url ) ) 123 { 124 return false; 125 } 126 } 127 ClassLoader parent = classloader.getParent(); 128 if( parent == null ) 129 { 130 return true; 131 } 132 else 133 { 134 return isaCandidate( parent, url ); 135 } 136 } 137 else 138 { 139 return true; 140 } 141 } 142 143 //-------------------------------------------------------------------- 144 // state 145 //-------------------------------------------------------------------- 146 147 private final Category m_category; 148 private final String m_name; 149 150 //-------------------------------------------------------------------- 151 // constructor 152 //-------------------------------------------------------------------- 153 154 /** 155 * Creation of a new classloader. 156 * @param name a name identifying the plugin 157 * @param category the classloader category identifier 158 * @param urls an array of urls to add to the classloader 159 * @param parent the parent classloader 160 */ 161 public StandardClassLoader( String name, Category category, URL[] urls, ClassLoader parent ) 162 { 163 super( urls, parent ); 164 m_category = category; 165 m_name = name; 166 } 167 168 //-------------------------------------------------------------------- 169 // StandardClassLoader 170 //-------------------------------------------------------------------- 171 172 /** 173 * Return the classloader category 174 * @return the classloader category 175 */ 176 public Category getCategory() 177 { 178 return m_category; 179 } 180 181 /** 182 * Return a string representation of the classloader. 183 * @return the string value 184 */ 185 public String getAnnotations() 186 { 187 StringBuffer buffer = new StringBuffer(); 188 ClassLoader parent = getParent(); 189 if( parent instanceof URLClassLoader ) 190 { 191 URLClassLoader urlClassLoader = (URLClassLoader) parent; 192 buffer.append( getURLClassLoaderAnnotations( urlClassLoader ) ); 193 } 194 buffer.append( " " ); 195 URL[] urls = getURLs(); 196 for( int i=0; i<urls.length; i++ ) 197 { 198 String path = urls[i].toString(); 199 if( !path.startsWith( "file:" ) ) 200 { 201 buffer.append( path ); 202 buffer.append( " " ); 203 } 204 } 205 return buffer.toString().trim(); 206 } 207 208 private String getURLClassLoaderAnnotations( URLClassLoader classloader ) 209 { 210 StringBuffer buffer = new StringBuffer(); 211 ClassLoader parent = classloader.getParent(); 212 if( ( null != parent ) && ( parent instanceof URLClassLoader ) ) 213 { 214 URLClassLoader urlClassLoader = (URLClassLoader) parent; 215 buffer.append( getURLClassLoaderAnnotations( urlClassLoader ) ); 216 } 217 if( ClassLoader.getSystemClassLoader() == classloader ) 218 { 219 return ""; 220 } 221 buffer.append( " " ); 222 URL[] urls = classloader.getURLs(); 223 for( int i=0; i<urls.length; i++ ) 224 { 225 String path = urls[i].toString(); 226 if( !path.startsWith( "file:" ) ) 227 { 228 buffer.append( path ); 229 buffer.append( " " ); 230 } 231 } 232 return buffer.toString().trim(); 233 } 234 235 /** 236 * Return a string representing of the classloader. 237 * @param expanded if true return an expanded representation of the classloader 238 * @return the string representation 239 */ 240 public String toString( boolean expanded ) 241 { 242 StringBuffer buffer = new StringBuffer(); 243 listClasspath( buffer ); 244 return buffer.toString(); 245 } 246 247 //-------------------------------------------------------------------- 248 // Object 249 //-------------------------------------------------------------------- 250 251 /** 252 * Return a string representing of the classloader. 253 * @return the string representation 254 */ 255 public String toString() 256 { 257 final String label = 258 getClass().getName() 259 + "#" 260 + System.identityHashCode( this ); 261 return label; 262 } 263 264 /** 265 * Internal operation to list the classloader classpath. 266 * @param buffer the buffer to list to 267 */ 268 protected void listClasspath( StringBuffer buffer ) 269 { 270 listClasspath( buffer, this ); 271 buffer.append( "\n" ); 272 } 273 274 /** 275 * Internal operation to list a classloader classpath. 276 * @param buffer the buffer to list to 277 * @param classloader the classloader to list 278 */ 279 protected void listClasspath( StringBuffer buffer, ClassLoader classloader ) 280 { 281 String label = 282 "\nClassLoader: " 283 + classloader.getClass().getName() 284 + " (" 285 + System.identityHashCode( classloader ) 286 + ")"; 287 288 if( classloader instanceof StandardClassLoader ) 289 { 290 StandardClassLoader cl = (StandardClassLoader) classloader; 291 ClassLoader parent = cl.getParent(); 292 if( null != parent ) 293 { 294 listClasspath( buffer, parent ); 295 } 296 297 if( null != m_name ) 298 { 299 label = label.concat( "\nLabel: " + cl.m_name + " " + cl.getCategory() ); 300 } 301 else 302 { 303 label = label.concat( "\nCategory: " + cl.getCategory() ); 304 } 305 buffer.append( label ); 306 buffer.append( "\n" ); 307 appendEntries( buffer, cl ); 308 } 309 else if( classloader instanceof URLClassLoader ) 310 { 311 URLClassLoader cl = (URLClassLoader) classloader; 312 ClassLoader parent = cl.getParent(); 313 if( null != parent ) 314 { 315 listClasspath( buffer, parent ); 316 } 317 buffer.append( label ); 318 appendEntries( buffer, cl ); 319 } 320 else 321 { 322 buffer.append( label ); 323 buffer.append( "]\n" ); 324 } 325 } 326 327 private static void appendEntries( StringBuffer buffer, URLClassLoader classloader ) 328 { 329 URL[] urls = classloader.getURLs(); 330 for( int i=0; i < urls.length; i++ ) 331 { 332 buffer.append( "\n " ); 333 URL url = urls[i]; 334 String spec = url.toString(); 335 buffer.append( spec ); 336 } 337 buffer.append( "\n" ); 338 } 339 340 /** 341 * Return a string representing a report fo the common classloader chain 342 * following by the primary annd seciondarty classloaders. 343 * @param primary the primary classloader 344 * @param secondary the secondary classloader 345 * @return the report 346 */ 347 public static String toString( ClassLoader primary, ClassLoader secondary ) 348 { 349 StringBuffer buffer = new StringBuffer(); 350 ClassLoader anchor = getCommonParent( primary, secondary ); 351 if( null != anchor ) 352 { 353 buffer.append( "\n----------------------------------------------------------------" ); 354 buffer.append( "\nCommon Classloader" ); 355 buffer.append( "\n----------------------------------------------------------------" ); 356 list( buffer, anchor ); 357 } 358 buffer.append( "\n----------------------------------------------------------------" ); 359 buffer.append( "\nPrimary Classloader" ); 360 buffer.append( "\n----------------------------------------------------------------" ); 361 list( buffer, primary, anchor ); 362 buffer.append( "\n----------------------------------------------------------------" ); 363 buffer.append( "\nSecondary Classloader" ); 364 buffer.append( "\n----------------------------------------------------------------" ); 365 list( buffer, secondary, anchor ); 366 buffer.append( "\n----------------------------------------------------------------" ); 367 return buffer.toString(); 368 } 369 370 private static ClassLoader getCommonParent( ClassLoader primary, ClassLoader secondary ) 371 { 372 ClassLoader[] primaryChain = getClassLoaderChain( primary ); 373 ClassLoader[] secondaryChain = getClassLoaderChain( secondary ); 374 return getCommonClassLoader( primaryChain, secondaryChain ); 375 } 376 377 private static ClassLoader[] getClassLoaderChain( ClassLoader classloader ) 378 { 379 if( null == classloader ) 380 { 381 return new ClassLoader[0]; 382 } 383 else 384 { 385 ArrayList list = new ArrayList(); 386 list.add( classloader ); 387 ClassLoader parent = classloader.getParent(); 388 while( null != parent ) 389 { 390 list.add( parent ); 391 parent = parent.getParent(); 392 } 393 ArrayList result = new ArrayList(); 394 int n = list.size() - 1; 395 for( int i=n; i>-1; i-- ) 396 { 397 result.add( list.get( i ) ); 398 } 399 return (ClassLoader[]) result.toArray( new ClassLoader[0] ); 400 } 401 } 402 403 private static ClassLoader getCommonClassLoader( ClassLoader[] primary, ClassLoader[] secondary ) 404 { 405 ClassLoader anchor = null; 406 for( int i=0; i<primary.length; i++ ) 407 { 408 ClassLoader classloader = primary[i]; 409 if( secondary.length > i ) 410 { 411 ClassLoader cl = secondary[i]; 412 if( classloader == cl ) 413 { 414 anchor = cl; 415 } 416 else 417 { 418 return anchor; 419 } 420 } 421 else 422 { 423 return anchor; 424 } 425 } 426 return anchor; 427 } 428 429 private static void list( StringBuffer buffer, ClassLoader classloader ) 430 { 431 list( buffer, classloader, null ); 432 } 433 434 private static void list( StringBuffer buffer, ClassLoader classloader, ClassLoader anchor ) 435 { 436 if( classloader == anchor ) 437 { 438 return; 439 } 440 ClassLoader parent = classloader.getParent(); 441 if( null != parent ) 442 { 443 list( buffer, parent, anchor ); 444 } 445 String label = 446 "\nClassLoader: " 447 + classloader.getClass().getName() 448 + " (" + System.identityHashCode( classloader ) + ")"; 449 buffer.append( label ); 450 if( classloader instanceof StandardClassLoader ) 451 { 452 StandardClassLoader loader = (StandardClassLoader) classloader; 453 if( null != loader.m_name ) 454 { 455 buffer.append( "\nLabel: " + loader.m_name + " " + loader.m_category ); 456 } 457 else 458 { 459 buffer.append( "\nCategory: " + loader.m_category ); 460 } 461 } 462 if( classloader instanceof URLClassLoader ) 463 { 464 URLClassLoader urlcl = (URLClassLoader) classloader; 465 buffer.append( "\n" ); 466 appendEntries( buffer, urlcl ); 467 } 468 } 469 470 /** 471 * Handle notification of the creation of a new classloader. 472 * @param logger the logging channel 473 * @param label the classloader label 474 * @param category the classloader category 475 * @param classloader the new classloader to report 476 */ 477 private static void classloaderConstructed( Logger logger, String label, Category category, ClassLoader classloader ) 478 { 479 if( logger.isTraceEnabled() ) 480 { 481 int id = System.identityHashCode( classloader ); 482 StringBuffer buffer = new StringBuffer(); 483 buffer.append( "new " ); 484 buffer.append( category.toString() ); 485 buffer.append( " classloader for " + label ); 486 buffer.append( "\n id: " + id ); 487 ClassLoader parent = classloader.getParent(); 488 if( null != parent ) 489 { 490 int pid = System.identityHashCode( parent ); 491 buffer.append( 492 "\n extends: " 493 + pid ); 494 } 495 if( classloader instanceof URLClassLoader ) 496 { 497 URLClassLoader loader = (URLClassLoader) classloader; 498 URL[] urls = loader.getURLs(); 499 if( urls.length == 1 ) 500 { 501 buffer.append( 502 "\n contains: 1 entry" ); 503 } 504 else 505 { 506 buffer.append( 507 "\n contains: " 508 + urls.length 509 + " entries" ); 510 } 511 for( int i=0; i < urls.length; i++ ) 512 { 513 URL url = urls[i]; 514 buffer.append( 515 "\n [" 516 + ( i+1 ) 517 + "] " 518 + url.toString() ); 519 } 520 } 521 logger.trace( buffer.toString() ); 522 } 523 } 524 }